// argsv/argument.hh
// Written by, Sohail Qayum Malik[sqm@cyberian.pk]

#include <algorithm>
#include <vector>

#include <stddef.h>

#ifndef		ARGUMENT_CONTAINER_HH
#define		ARGUMENT_CONTAINER_HH

#include "exception.hh"
#include "concept_checker.h"
#include "args.h"

#include "arg_iter.hh"

extern "C" void iterargs(void);

/*
// Implementation of Random Access Container
// Random Access Container is a refinement of Reversible Container
// Reversible Container is a refinement of Forward Container
// Forward Container is a refinement of Container, Equality Comparable and
// LessThanComparable
// Container is refinement of Assignable
*/

template<class Tp> 
class argument {

	public:	
		//The contained type must be assignable, but need not be
		//DefaultConstructible
		typedef 	typename  Tp::value_type	value_type;

		//The iterator, which is being used
		//Okhey it is hardcoded, I should've used the
		//template for this type too
		//But doing so does a very circular type definition
		//The Iterator should have Random_Access_Iterator_tag
	        typedef	        arg_iter< argument<Tp>, Tp > iterator;
		//Iterator of same type but only to read and not write
		//This has to be a completely different Iterator definition
		typedef		const arg_iter< argument<Tp>, Tp > const_iterator;
		typedef		argument<Tp>&			reference;
		typedef		const argument<Tp>&		const_reference;
		typedef		argument<Tp>*			pointer;
//		typedef	        typename  Tp::reference	reference;
//		typedef		typename  Tp::pointer		pointer;
		typedef		size_t				difference_type;
		typedef		size_t				size_type;

	private:

		const int argc;
		char const*const*const argv;
		const std::string commands;
		typename std::vector<value_type>::size_type currentLocation;

		struct argsv{
                   bool flag_set_when_arguments_are_given_at_cli;		
                   int iRefCount;
		   std::vector<value_type> *args_vector;
		   struct {
	              int argc; 		   
                   } common;			   
		} *args;
	
	public:	
		//char* ptr = "Jane, this is Internet.";
	        //char const *const argv[], this will stop...
		//*const, *argv = ptr; and...
		//char const, *(argv[0] + 0) = ''; 
		//But still this(following) is possible...
		//argv = &ptr;
		//So the final signature for the last argument is...
		//Random Access Container is not DefaultConstructible
		argument<Tp>(const int& argc, char const*const*const argv, std::string commands) : argc(argc), argv(argv), commands(commands), currentLocation(0) {

	           // Just some counters and old position holders
		   bool redundency_flag = 0; //Silly, no not silly but a huge realization that redundency is creating problems;
                   int count;
		   size_t old_pos;
		   typename std::vector<value_type>::size_type vec_index = 0;
                   /*
		    * More than one iterator can be active at the same time
		    * in this container. Each Iterator will have its own 
		    * instance of this container. All instances of this 
		    * container will share the single(refrence counted) 
		    * instance of inner struct argsv
		    */           			
	           int index = 0, cmds = 1, appears;
		   size_t pos = 0, cmt_head_pos, cmt_size;

		   int largc = argc;
	 					  
		   typename std::vector<value_type>::iterator it;
		   typename std::vector<value_type>::reverse_iterator rit;
		   // Just some counters and old position holders end here

                   args = new argsv();
		   args->iRefCount = 1;
		   args->flag_set_when_arguments_are_given_at_cli = 0;
		   args->common.argc = 0;
                   args->args_vector = new std::vector<value_type>();

                   do {

                      if (pos)
                         pos++;			      

                      /*
		       The last two arguments are...(the cmds and...
		       SHORT_COMMANDS/LONG_COMMANDS)
		       The command index(1 origin), the index is into COMMANDS
		       macro
		       The long commands(1) and short commands(0)
		       Short command is -ss atleast two, a single -s is long 
		       command
		       */
		      while ((count=getCommandLongShort(commands, argc - index, argv + index, cmds, SHORT_COMMANDS, &appears, &cmt_head_pos, &cmt_size)) > -1) {

	                 index += count + 1;

/*			 
			 if (!args->common.argc)
                            args->common.argc = index - 1;
*/			    
			 if (redundency_flag) {

		            args->args_vector->push_back(value_type(argv[index - 1], index - 1/* Previously it was just index */, appears, cmds, pos, cmt_head_pos, cmt_size, redundency_flag));
			 }
			 else {

			    args->args_vector->push_back(value_type(argv[index - 1], index - 1/* Previously it was just index */, appears, cmds, pos, cmt_head_pos, cmt_size));
			 }

			 redundency_flag = 1;
			 args->flag_set_when_arguments_are_given_at_cli = 1;
                      }

		      index = 0;
		      redundency_flag = 0;

		      /* 
		       The last two arguments are...
		       The command index(1 origin), the index is into COMMANDS
		       macro
		       The long commands(1) and short commands(0)
		       Short command is -ss atleast two, a single -s is long 
		       command
		       */
		      while ((count=getCommandLongShort(commands, argc - index, argv + index, cmds, LONG_COMMANDS, /*&appears*/ NULL, &cmt_head_pos, &cmt_size)) > -1) {

			 index += count + 1;

/*			 
			 if (!args->common.argc)
                            args->common.argc = index - 1;
*/

			 if (redundency_flag) {

		            args->args_vector->push_back(value_type(argv[index - 1], index - 1 /* Previously it was just index */, 0, cmds, pos, cmt_head_pos, cmt_size, redundency_flag));				 
                         }
			 else {

                            args->args_vector->push_back(value_type(argv[index - 1], index - 1 /* Previously it was just index */, 0, cmds, pos, cmt_head_pos, cmt_size));
			 }

			 redundency_flag = 1;
			 args->flag_set_when_arguments_are_given_at_cli = 1;
                      }

		      index = 0;
		      redundency_flag = 0;

/*		      
		      if (pos)  
                         pos++;
*/			 

		      for (it = args->args_vector->begin(); it < args->args_vector->end(); it++) {

                          if (*it == cmds)
                             break;				  

                      }

		      // This command is not vectored
		      if (it == args->args_vector->end())  
			 args->args_vector->push_back(value_type(NULL, 0, 0, cmds, pos, cmt_head_pos, cmt_size));      
                       			   		                   		 	      cmds++;

		      old_pos = pos;

		      pos = commands.find(COMMAND_DELIMITER, pos);

		   } while (pos != std::string::npos);

/*		   
		   if (!args->common.argc)
                      args->common.argc = argc;
*/		      

		   /* Sorting happens here, and it puts all instances which are 0 at the top of the vector */
		   sort(args->args_vector->begin(), args->args_vector->end());

		   rit = args->args_vector->rbegin(); 
                   
		   while ((rit + vec_index) != args->args_vector->rend() - 1) {
 
		      if ((*(rit + vec_index)).get_index()) {

			 //cout<<(*(rit + vec_index)).get_arg()<<endl;

			 args->common.argc = (*(rit + vec_index)).get_index();     
		         *(rit + vec_index) = largc - (*(rit + vec_index)).get_index();
			 if ((*(rit + vec_index)).get_index() != (*(rit + vec_index + 1)).get_index())
		            largc = largc - (largc - (*(rit + vec_index)).get_index());
                      }		      

                      vec_index++;			   
                   }

		   if ((*(rit + vec_index)).get_index()) {
		      *(rit + vec_index) = largc - (*(rit + vec_index)).get_index();
		      args->common.argc = (*(rit + vec_index)).get_index();
                   }

		   if (!args->common.argc)
                      args->common.argc = argc;			   
                   			   
	        }

		//Random Access Container is not DefaultConstructible
		argument<Tp>(typename std::vector<value_type>::size_type currentLocation, const int argc, char const*const*const argv, const std::string& commands, struct argsv *args) : currentLocation(currentLocation), argc(argc), argv(argv), commands(commands), args(args) {

		   args->iRefCount++;
                }			
		
		argument<Tp>(const argument<Tp>& ref) : currentLocation(ref.currentLocation), argc(ref.argc), argv(ref.argv), commands(ref.commands), args(ref.args) {

                   args->iRefCount++; 					 
                }
	
		~argument<Tp>(void) {

  		    if (args->iRefCount == 1) {

		       delete args->args_vector;
		       delete args;
		    }
		    else 
                       args->iRefCount--; 			    
		}
	
		// Container type
		// Index at the begining
        	iterator begin(void) {

/*			
                   argument<Tp> tmp = argument<Tp>(0, argc, argv, commands, args);
		   cout<<"begin = "<<tmp.currentLocation<<endl;
*/		   

		   return iterator(argument<Tp>(0, argc, argv, commands, args));
		}

		// Index at the end
		iterator end(void) {


/*			
                   argument<Tp> tmp = argument<Tp>(args->args_vector->size(), argc, argv, commands, args);

		   cout<<"end = "<<tmp.currentLocation<<endl;
*/		   

/*
                   if (args->flag_set_when_arguments_are_given_at_cli)
		      return iterator(argument<Tp>(args->args_vector->size(), argc, argv, commands, args));
		   else 
		      return iterator(argument<Tp>(0, argc, argv, commands, args));
*/
                   return iterator(argument<Tp>(args->args_vector->size(), argc, argv, commands, args));			
		}   

		size_type size(void) {

		   return args->args_vector->size();  	
		}

		size_type max_size(void) {

                   return args->args_vector->max_size();
		}	
	        
		bool empty(void) {

                   return args->args_vector->empty();
		}

           //A Reversible container is refinement of a Forward Container
	   //A type is Forward Container if it is a refinement of Container,
	   //Equality Comparable type and LessThan Comparable type

           //A Container is a refinement of Assignable type 
	   //Rest of the detail deals with copy constructors
	   argument<Tp> &operator =(argument<Tp> &rvalue) {

              return *this;       			
           }			
                
           void swap(argument<Tp>& other) {
	   }
	   
           //Equality Comparable
	   bool operator ==(argument<Tp>& oprand) {

              bool ret = 0;

	      if (args == oprand.args) {

                 if (!args->flag_set_when_arguments_are_given_at_cli || currentLocation == oprand.currentLocation) 
                    ret = 1;                    			 
              }		      

	      return ret;
           }

	   bool operator !=(argument <Tp>& oprand) {
	
              return !(*this == oprand);		   
           }		   
           		
           //LessThan Comparable	  
	   bool operator < (argument<Tp>& oprand) {
              
	      //Logic to comapre *this and oprand goes here
	      return 1;
           }
	   	
	   bool operator > (argument<Tp>& oprand) {
	 
	      return oprand < *this;  	   
           }

	   bool operator <= (argument<Tp>& oprand) {

              return !(oprand < *this);		   
           }

	   bool operator >= (argument<Tp>& oprand) {
		 
	      return !(*this < oprand);	   
           }		   
	  
           // Non container specific definitions
	   value_type operator [](const int n) {

              value_type foo;
               
	      typename std::vector<value_type>::iterator itB = args->args_vector->begin();
	      typename std::vector<value_type>::iterator itE = args->args_vector->end();
         
	      if (COMMON == n) {

                 foo = value_type(NULL, 0, 0, 0, 0, 0, 0, 0, args->common.argc);
              }		 
	      else { 

	         while (itB != itE) {

                    //cout<<"yes"<<endl;
		 
                    if (*itB == n) 
                       return *itB;
                 		    
                    itB++;	      
                 }
              }

	      return foo;
           }		   
	   
	   value_type operator *(void) {
		   
	      //This is why the contained type should be default constructible too. The instance of contained type thus returned will indicate if the contained type(instance) returned is a actual value or some other value which indicates that the acutal desired value is not found  
              value_type foo;

	      //int num = 0;

              typename std::vector<value_type>::iterator it = args->args_vector->begin();

	      //while ((it + num) < args->args_vector->end()) {

              //   foo = it[num];

	      // 	 cout<<foo.get_index()<<", "<<foo.get_argc()<<endl;

              //   num++;	       	      
              //}  		      
		      			  
	      if (size()) {
		  
		        // cout<<"1"<<endl;
                 foo = it[currentLocation];
				 //cout<<"2"<<endl;
		  }		 
              		 
	      return foo;
           }		   

	   /* Pre increment */
	   argument<Tp> &operator ++(void) {

              if (!empty() && currentLocation < size())
                 currentLocation++;		      
		                   		    
              return *this; 		   
           }		   
	  
	   /* Post increment */
	   argument<Tp> operator ++(int /* Unused */) {

              argument<Tp> tmp = *this;

	      ++(*this); 

              return tmp;               		   
           }		   

	   // The contained type should follow the following constraints
	   static void constraints(value_type a, value_type b) {

	      value_type c("All play no work makes jack a mere toy. All work and no play makes jack a dull boy", 0, 0, 0,     0, 0, 0);
	      const value_type d;
	      value_type e(d);
		  
	      (void)(a < b);
	      (void)(a == b);
	      (a == 0);
	      (b != 0);
	      a = 0;
	      a() && b();
	      a(0) && b(1);
	   } 

           THIS_CLASS_REQUIRES(value_type, argument); 	   

	   friend void iterargs(void);

	   static argument* NewE(const int& argc, char const*const*const argv, std::string commands) throw (args_exception){
 
	      /* self, CPython FrameWork semantics */
              pointer self;
        		   
              try {

                 self = new argument<Tp>(argc, argv, commands);

		 if (self == NULL) 
		    throw (args_exception("args_exception was raised\n"));			 
                 constructE(self, argc, argv, commands);

		 return self;
	      }
	      catch (std::bad_alloc &excp) {

		 throw (args_exception(std::string(excp.what())));      
              }
	      catch (args_exception &excp) {

                 throw (args_exception(excp));		      
              }  		      
           }

	   static void constructE(pointer self, const int& argc, char const*const*const argv, std::string commands) throw(args_exception){
	   } 	   

///////////////////////////////////////////////////////////////////////////////    	   // The non-container specific functions begin here //
	   /////////////////////////////////////////////////////
           int getCommandLongShort(std::string& commands, int argc, \
			         char const*const*const argv, size_t cmd, \
				 bool longShortOrShort, int *appears, size_t *cmt_head_pos, size_t *cmt_size) {

	      size_t pos, old_pos = 0, size = commands.size(), count = 0;

	      int index;

	      while(old_pos < size || cmd != 0) {

                 count++;

                 pos = commands.find(COMMAND_DELIMITER, old_pos); 	

	         if(pos == std::string::npos)
		    pos = size;

	//cout<<"----> "<<commands.substr(old_pos, pos - old_pos)<<endl;   
		 if(count == cmd) {
	            if(longShortOrShort) {
//		       cout<<"Long"<<endl;
                       index = getCommandLongShort1(commands.substr(\
					        old_pos, pos - old_pos), \
				                argc, argv, cmt_head_pos, \
						                    cmt_size); 
//		       cout<<index<<endl; 
		    }   
		    else {
//		       cout<<"Short"<<endl;	    
		       index = getCommandLongShort2(commands.substr(\
					        old_pos, pos - old_pos), \
				                argc, argv, appears, \
						      cmt_head_pos, cmt_size); 
//		       cout<<index<<endl;  
		    }   
		           
		       /*
			* old_pos = where command start at an absolute index
			* old_pos + cmt_head_pos = start of comment at an 
			*                          absolute index
			* cmt_size = size of the comment
			* The cmt_size can be zero
			* The cmd_head_pos can be string::npos(No comment)
			* When there is no comment then it is certain that...
			* cmt_size is zero(& cmd_head_pos is string::npos)
			*/
		       /* 
			* commands.substr(old_pos + ((cmt_head_pos == (string::npos)) ? 0 : cmt_head_pos) , cmt_size)
			*/
		      
		       
                    return index;
                 }

		 old_pos = pos + COMMAND_DELIMITER_SIZE; 
	      } 
              
	      //The following statement will not be reached, but if cmd == 0
              return -1;		   
           }

	   int getCommandLongShort2(std::string commands, int argc, \
			              char const*const*const argv, \
				      int *appears, size_t *cmt_head_pos, \
				                          size_t *cmt_size) {
	

	      size_t old_pos = 0, pos, same_command_size, \
			                          size = commands.size();

	      *cmt_head_pos = commands.find(COMMENT_HEAD_SYMBOL, 0);
	      *cmt_size = commands.size() - \
			  ((*cmt_head_pos != std::string::npos) ? *cmt_head_pos \
			                                    : commands.size());	      
              commands = commands.substr(0, *cmt_head_pos); 

	      while(old_pos < size) {

	         pos = commands.find(SAME_COMMAND_DELIMITER, old_pos);
		 if(pos == std::string::npos)
		    pos = size;

		 same_command_size = commands.substr(old_pos, pos - \
				                          old_pos).size();

 //                cout<<same_command_size<<" -- "<<commands.substr(old_pos, pos - old_pos)<<endl;

		 if(same_command_size > MAX_SHORT_COMMAND_SIZE && !commands.substr(old_pos, 1).compare("-") && commands.substr(old_pos + 1, 1).compare("-")) {


//	            cout<<"-->* "<<commands.substr(old_pos, pos - old_pos)<<endl;
		    return getCommandLongShort3(commands.substr(old_pos + 1, pos - 1 - old_pos), argc, argv, appears);
		 }		 

//		 if(same_command_size && same_command_size <= MAX_SHORT_COMMAND_SIZE) {

//	            cout<<commands.substr(old_pos, pos - old_pos)<<endl; 
//	            return getCommandLongShort3(commands.substr(old_pos, pos - old_pos), argc, argv, appears);
//                 }			 
		
		 old_pos = pos + SAME_COMMAND_DELIMITER_SIZE;
	      }  	      
               		    
              return -1;		   
           }

	   int getCommandLongShort3(std::string commands, int argc, \
			                  char const*const*const argv, \
					                    int *appears) {
		 
	      int index, old_pos = 0, pos;

//	      cout<<commands<<endl;

	      for(index = 0; index < argc; index++) {
		   
		 *appears = 0;

		 std::string argv_command(argv[index]);

		 if(argv_command.size() > MAX_SHORT_COMMAND_SIZE*2 && *argv[index] == '-' && *(argv[index] + 1) != '-') {
			 
//		    cout<<"--> --> "<<argv_command<<endl;   	 

		    do {

		       pos = argv_command.find(commands, old_pos);

		       if(pos != std::string::npos) 
			  *appears+=1;     
		        
		       old_pos = pos + MAX_SHORT_COMMAND_SIZE;

		    } while(pos != std::string::npos); 
                 }			 

/*		 
		 
		 if(*argv[index] == '-') {
			 

                    string argv_command((argv[index] + 1));

		    if(argv_command.size() > MAX_SHORT_COMMAND_SIZE) {

		       do { 	    
                          pos = argv_command.find(commands, old_pos);

			  if(pos != string::npos) {
//		             cout<<"--> "<<argv_command<<endl;		  
                             *appears+=1;
			  }   
				   
			  old_pos = pos + MAX_SHORT_COMMAND_SIZE;	   
		       } 
		       while(pos != string::npos);
*/		       
		       

		       if(*appears)
			  return index;  
/*		       
		    }    
                 }
*/		 
              }		      
		 
	      return -1;	   
	   } 	   
 
	   int getCommandLongShort1(std::string commands, int argc, \
			                  char const*const*const argv, size_t *cmt_head_pos, size_t *cmt_size) {

              size_t pos, old_pos = 0, size = commands.size();     
	       	   
	      int index;

	      *cmt_head_pos = commands.find(COMMENT_HEAD_SYMBOL, 0);
	      *cmt_size = commands.size() - \
			  ((*cmt_head_pos != std::string::npos) ? *cmt_head_pos \
			                                    : commands.size());

	      commands = commands.substr(0, *cmt_head_pos);

	      for(index = 0; index < argc; index++) {
		 
		 while(old_pos < size) {
		
	            pos = commands.find(SAME_COMMAND_DELIMITER, old_pos);

		    if(pos == std::string::npos)
		       pos = size;
//                    cout<<commands.substr(old_pos, pos - old_pos)<<" "<<argv[index]<<endl;
		    if((commands.substr(old_pos, pos - \
				  old_pos)).compare(argv[index]) == 0) 
		       return index;	    
	         		     
		    old_pos = pos + 1;
	         }		 
                 
		 old_pos = 0;
	      }	      
	
              return -1; 		   
           }		   
};

#endif
